package com.jeremyaburns.oddio 
{
	import flash.events.EventDispatcher;
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	
	public class Song extends EventDispatcher
	{
		public var instruments :Vector.<Instrument>;
		public var tracks      :Vector.<Track>;
		
		public var tempo :int;
		
		
		// Tempo is the number of samples played in a single timeIndex.
		public function Song( tempo:int=5000 ) {
			this.tempo  = tempo;
			this.instruments = new Vector.<Instrument>();
			this.tracks = new Vector.<Track>();
		}

		
		
		
		
		private var playbackSound  :Sound;
		private var playbackChan   :SoundChannel;
		private var playbackSample :TrackSample;
		
		// Plays the song.
		public function play() :void
		{
			// If already playing, stop.
			if( playbackSound ) stop();
			
			// Prepare the tracks for playback.
			for each( var t:Track in tracks ) t.prepareForPlayback( tempo );
			playbackSample = new TrackSample();

			// Create a sound and attach a SampleDataEvent listener so we can build it in real time.
			playbackSound = new Sound();
			playbackSound.addEventListener( SampleDataEvent.SAMPLE_DATA, playbackSound_sampleData );
			playbackChan = playbackSound.play();			
		}		
		
		// Stops playback if currently  is being played.
		public function stop() :void
		{
			if( playbackSound == null ) return;
			playbackSound.removeEventListener( SampleDataEvent.SAMPLE_DATA, playbackSound_sampleData );
			playbackSound = null;
		//	playbackChan = null;
		}
		
		
		public function get playhead() :int { return playbackChan ? 1 + 44.1*playbackChan.position/tempo : 0 ; }
		
			
		// This is called by Flash to fetch raw audio data to output.
		private function playbackSound_sampleData( e:SampleDataEvent ) :void
		{
			for( var i:int = 0; i < 8192; i++ )
			{
				playbackSample.left  = 0;
				playbackSample.right = 0;
				
				var atLeastOneTrackAlive:Boolean = false;
				for( var j:int = 0 ; j < tracks.length ; ++j ) {
					var a:Boolean = tracks[j].addNextSample( playbackSample );
					atLeastOneTrackAlive = a || atLeastOneTrackAlive;
				}
				
				if( atLeastOneTrackAlive ) {
					e.data.writeFloat( playbackSample.left  );
					e.data.writeFloat( playbackSample.right );
				} else {
					stop();
				}
			}
		}	
	}
}